/*
 * Copyright (C) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef IMAGE_KNIFE_C_KEKE_PROMISE_NAPI_H
#define IMAGE_KNIFE_C_KEKE_PROMISE_NAPI_H

#include <string>
#include <arkui/native_node_napi.h>
#include <multimedia/image_framework/image/pixelmap_native.h>
#include "promise.h"
#include "log.h"
#include "task_worker.h"

namespace ImageKnifePro {


template <typename T>
class PromiseNapiBase : public Promise<T> {
public:
    napi_value GetPromise()
    {
        return promise_;
    }

    napi_value Reject(std::string info)
    {
        if (!this->IsResolved()) {
            IMAGE_KNIFE_LOG(LOG_ERROR, "%{public}s", info.c_str());
            napi_value strValue;
            napi_create_string_utf8(env_, info.c_str(), NAPI_AUTO_LENGTH, &strValue);
            napi_reject_deferred(env_, deferred_, strValue);
        }
        return promise_;
    }
protected:
    PromiseNapiBase() : env_(TaskWorker::GetInstance()->GetEnv())
    {
        napi_status status = napi_create_promise(env_, &deferred_, &promise_);
        IMAGE_KNIFE_CHECK(status != napi_ok, ("Create Promise Failed:" + std::to_string(status)).c_str());
    }

    napi_env env_;
    napi_value promise_;
    napi_deferred deferred_;
};

template <typename T>
class PromiseNapiThreadSafe : public PromiseNapiBase<T> {
private:
    void SetValue(T value) final
    {
        if (TaskWorker::GetInstance()->IsMainThread()) {
            ResolveDeferred(value);
        } else {
            auto self = std::static_pointer_cast<PromiseNapiThreadSafe<T>>(this->shared_from_this());
            TaskWorker::GetInstance()->ToMainThread([self, value](void*) {self->ResolveDeferred(value);}, nullptr);
        }
    }

    virtual void ResolveDeferred(T value) = 0;
};

template <>
class PromiseNapiThreadSafe<void> : public PromiseNapiBase<void> {
private:
    void SetValue() final
    {
        if (TaskWorker::GetInstance()->IsMainThread()) {
            ResolveDeferred();
        } else {
            auto self = std::static_pointer_cast<PromiseNapiThreadSafe<void>>(this->shared_from_this());
            TaskWorker::GetInstance()->ToMainThread([self](void*) {self->ResolveDeferred();}, nullptr);
        }
    }

    virtual void ResolveDeferred() = 0;
};

// 默认模板空实现，无法使用
template <typename T>
class PromiseNapi {
public:
    PromiseNapi() = delete;
};

template<>
class PromiseNapi<int> : public PromiseNapiThreadSafe<int> {
private:
    void ResolveDeferred(int value) override
    {
        napi_value numValue;
        napi_create_int32(env_, value, &numValue);
        napi_resolve_deferred(env_, deferred_, numValue);
    }
};

template <>
class PromiseNapi<void> : public PromiseNapiThreadSafe<void> {
private:
    void ResolveDeferred() override
    {
        napi_value undefined;
        napi_get_undefined(env_, &undefined);
        napi_resolve_deferred(env_, deferred_, undefined);
    }
};

template <>
class PromiseNapi<std::string> : public PromiseNapiThreadSafe<std::string> {
private:
    void ResolveDeferred(std::string value) override
    {
        napi_value strValue;
        napi_create_string_utf8(env_, value.c_str(), NAPI_AUTO_LENGTH, &strValue);
        napi_resolve_deferred(env_, deferred_, strValue);
    }
};

template <>
class PromiseNapi<std::shared_ptr<ImageData>> : public PromiseNapiThreadSafe<std::shared_ptr<ImageData>> {
private:
    void ResolveDeferred(std::shared_ptr<ImageData> imageData) override
    {
        if (imageData == nullptr) {
            IMAGE_KNIFE_LOG(LOG_ERROR, "PromiseNapi:: imageData == nullptr");
            napi_value undefined;
            napi_get_undefined(env_, &undefined);
            napi_resolve_deferred(env_, deferred_, undefined);
            return;
        }

        napi_value jsImageData;
        napi_create_object(env_, &jsImageData);

        int frameCount = imageData->GetFrameCount();
        napi_value value;
        napi_create_int32(env_, frameCount, &value);
        napi_set_named_property(env_, jsImageData, "frameCount", value);

        OH_PixelmapNative **pixelmapList = imageData->GetPixelmapList();
        if (pixelmapList != nullptr) {
            napi_value pixelmapListValue;
            napi_create_array_with_length(env_, frameCount, &pixelmapListValue);
            for (int i = 0; i < frameCount; i++) {
                napi_value pixelmap;
                OH_PixelmapNative_ConvertPixelmapNativeToNapi(env_, pixelmapList[i], &pixelmap);
                napi_set_element(env_, pixelmapListValue, i, pixelmap);
            }
            napi_set_named_property(env_, jsImageData, "pixelmapList", pixelmapListValue);
        }

        int *delayTimeList = imageData->GetDelayTimeList();
        if (delayTimeList != nullptr) {
            napi_value timeList;
            napi_create_array_with_length(env_, frameCount, &timeList);
            for (int i = 0; i < frameCount; i++) {
                napi_value number;
                napi_create_int32(env_, delayTimeList[i], &number);
                napi_set_element(env_, timeList, i, number);
            }
            napi_set_named_property(env_, jsImageData, "delayTimeList", timeList);
        }

        napi_resolve_deferred(env_, deferred_, jsImageData);
    }
};
}
#endif //IMAGE_KNIFE_C_KEKE_PROMISE_NAPI_H
